package com.wandian.texas.demo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
	/**   
	* @author long
	* @QQ 108232706
	* @version 1.0   
	*/   
	public class NIOServer {

	private static int BLOCK = 1024;
//	private static String name = "";
	public Selector selector;
	protected ByteBuffer clientBuffer = ByteBuffer.allocate(BLOCK);
	protected CharsetDecoder decoder;
	public static CharsetEncoder encoder = Charset.forName("GB2312")
			.newEncoder();

	public HashMap<Integer, SelectionKey> rooms = new HashMap<Integer, SelectionKey>();
	public List<Integer> ids = new ArrayList<Integer>();
	private final HashMap<Integer, ICommand> commands = new HashMap<Integer, ICommand>();

	public final int CONNECT = 1000;
	public final int SELECT_ROOM = 1005;
	public final int START = 1010;
	public final int RECEIVE_DATA = 1015;

	public final int CONNECT_SUCCESS = 2000;
	public final int SEND_DATA = 2005;

	public NIOServer(int port) throws IOException {
		selector = this.getSelector(port);
		Charset charset = Charset.forName("GB2312");
		decoder = charset.newDecoder();
	}

	public void registerCommand(int commandID, ICommand command) {
		if (!commands.containsKey(commandID)) {
			System.out.println("registeCommand  " + commandID + "  ,  "
					+ command);
			commands.put(commandID, command);
		}
	}

	protected Selector getSelector(int port) throws IOException {
		ServerSocketChannel server = ServerSocketChannel.open();
		Selector selector = Selector.open();
		server.socket().bind(new InetSocketAddress(port));
		server.configureBlocking(false);
		server.register(selector, SelectionKey.OP_ACCEPT);
		return selector;
	}

	public void listen() {
		try {
			while (true) {
				 selector.select();
				Iterator<SelectionKey> iter = selector.selectedKeys()
						.iterator();
				while (iter.hasNext()) {
					SelectionKey key = iter.next();
					iter.remove();
					process(key);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	protected void process(SelectionKey key) throws IOException {
		if (key.isAcceptable()) {
			ServerSocketChannel server = (ServerSocketChannel) key.channel();
			SocketChannel channel = server.accept();

			channel.configureBlocking(false);
			channel.register(selector, SelectionKey.OP_READ);
		} else if (key.isReadable()) {
			read(key);

		} else if (key.isWritable()) {
			if (key.isValid()) {

				Packet p = new Packet(100);
				p.writeInt(300);
				p.writeInt(400);
				p.writeString("Ұݹ", "utf-8");
				send(key, p);

			}
		}
	}

	/**
	 * ȡ
	 * 
	 * @param channel
	 * @throws IOException
	 */
	public void read(SelectionKey key) throws IOException {
		SocketChannel channel = (SocketChannel) key.channel();
		int count = channel.read(clientBuffer);
		if (count > 0) {
			clientBuffer.flip();
			Packet packet = new Packet(clientBuffer);

			packet.readInt();

			int command = packet.readShort();

			if (commands.containsKey(command)) {
				ICommand comm = commands.get(command);
				int result = comm.execute(this, channel, packet);
				key.attach(result);

			}

			clientBuffer.clear();
		}
	}

	/**
	 * 
	 * 
	 * @param channel
	 * @param packet
	 * @throws IOException
	 */
	public int send(SelectionKey key, Packet packet) throws IOException {
		return send(key, packet.byteBuffer());
	}

	/**
	 * 
	 * 
	 * @param key
	 * @param buffer
	 * @return Bytesܳ
	 * @throws IOException
	 */
	public int send(SelectionKey key, ByteBuffer buffer) throws IOException {
		SocketChannel channel = (SocketChannel) key.channel();

		int dataLen = buffer.limit() - buffer.remaining();

		if (buffer.position() > 0) {
			buffer.flip();
		}

		ByteBuffer bts = ByteBuffer.allocate(dataLen + 4);

		bts.putInt(dataLen);

		bts.put(buffer);

		if (bts.position() > 0) {
			bts.flip();
		}
		int l = channel.write(bts);
		bts.clear();
		buffer.clear();

		channel.register(selector, SelectionKey.OP_READ);

		key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
		return l;
	}

	public static void main(String[] args) {
		int port = 8088;
		try {
			NIOServer server = new NIOServer(port);
			server.registerCommand(1000, new ConnectCommand());
			System.out.println("listening on " + port);

			server.listen();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}